unit tilepalettedrawerunit;

{< Additional routines for drawing tile palette (either for the tile palette
  proper or for Tilesets page in the database). }

{$mode objfpc}{$H+}

interface

uses
  Classes, SysUtils, Graphics, database, fpjson, tilesetgraphicsunit,
  BGRABitmapTypes, BGRABitmap, constants, palettehelper;


type
  { Class for drawing palette. It loads tileset from disk separately from the
    map view (but doesn't keep it in memory: it draws the palette and keeps the
    palette in memory), if this turns out bad performance-wise it might be nice
    to share the tileset images. }
  TTilePaletteDrawer = class
  private
    TilesetGraphics: TTilesetGraphics;
  public
    function MakePaletteBitmap(TileType: Integer; ATilesetGraphics: TTilesetGraphics; IncludeBackgrounds: Boolean = False): TBGRABitmap;

    function CreateBitmapForPalette(TileType: Integer): TBGRABitmap;
    { Returns height of palette in tiles }
    function GetPaletteHeight(TileType: Integer): Integer;
    function DrawTilesA(IncludeBackgrounds: Boolean): TBGRABitmap;
    function DrawUpperTiles(TileType: Integer): TBGRABitmap;
  end;

implementation

uses
  globals, Dialogs;

function TTilePaletteDrawer.MakePaletteBitmap(TileType: Integer;
  ATilesetGraphics: TTilesetGraphics; IncludeBackgrounds: Boolean = False): TBGRABitmap;
begin
  TilesetGraphics := ATilesetGraphics; //TODO: pass that as an argument?

  if TileType = TILE_TYPE_A then
    MakePaletteBitmap := DrawTilesA(IncludeBackgrounds)
  else
    MakePaletteBitmap := DrawUpperTiles(TileType);
end;

function TTilePaletteDrawer.CreateBitmapForPalette(TileType: Integer): TBGRABitmap;
var
  Bitmap: TBGRABitmap;
begin
  Bitmap := TBGRABitmap.Create(8 * 48, GetPaletteHeight(TileType) * 48); //TODO: allow other tile sizes
  Bitmap.CanvasBGRA.Brush.Color := ColorToBGRA(ColorToRGB(clForm));
  Bitmap.CanvasBGRA.FillRect(0, 0, Bitmap.Width, Bitmap.Height);
  CreateBitmapForPalette := Bitmap;
end;

function TTilePaletteDrawer.DrawTilesA(IncludeBackgrounds: Boolean): TBGRABitmap;
var
  ResBmp: TBGRABitmap;
  OffsetYInTiles: Integer;

  procedure DrawAutotileAsBackground(Tileset: TBGRABitmap; Dst: TRect; AtX, AtY: Integer);
  var
    Src: TRect;
  begin
    Src.Left   := AtX * (48 * 2) + 24;
    Src.Top    := AtY * (48 * 3) + 48+24;
    Src.Right  := Src.Left + 48;
    Src.Bottom := Src.Top + 48;

    ResBmp.CanvasBGRA.CopyRect(Dst, Tileset, Src);
  end;

  procedure DrawTilesA1;
  var
    I, X, Y: Integer;
    SB: TAFirstPaletteDesc;

    Src, Dst: TRect;
    Tileset: TBGRABitmap;

    procedure DrawNormalAT;
    begin
      Dst.Left   := X; //TODO: adapt for different tile widths
      Dst.Top    := Y; //TODO: adapt for different tile heights
      Dst.Right  := X + 48;
      Dst.Bottom := Y + 48;

      if IncludeBackgrounds and SB.OverWater then
        DrawAutotileAsBackground(Tileset, Dst, 0, 0);

      Src.Left   := (SB.SubBlockId mod 8) * (48 * 2);
      Src.Top    := (SB.SubBlockId div 8) * (48 * 3);
      Src.Right  := Src.Left + 48;
      Src.Bottom := Src.Top + 48;

      ResBmp.CanvasBGRA.CopyRect(Dst, Tileset, Src);
    end;

    procedure DrawWaterfall;
    begin
      Dst.Left   := X; //TODO: adapt for different tile widths
      Dst.Top    := Y; //TODO: adapt for different tile heights
      Dst.Right  := X + 48;
      Dst.Bottom := Y + 88;

      if IncludeBackgrounds and SB.OverWater then begin
        DrawAutotileAsBackground(Tileset, Rect(X, Y, X + 48, Y + 48), 0, 0);
      end;

      Dst := Rect(X, Y, X + 24, Y + 48);

      Src.Left   := (SB.SubBlockId mod 8) * (48 * 2);
      Src.Top    := (SB.SubBlockId div 8) * (48 * 3);
      Src.Right  := Src.Left + 24;
      Src.Bottom := Src.Top + 48;

      ResBmp.CanvasBGRA.CopyRect(Dst, Tileset, Src);

      Dst := Rect(X + 24, Y, X + 48, Y + 48);

      Src.Left   := (SB.SubBlockId mod 8) * (48 * 2) + 48 + 24;
      Src.Right  := Src.Left + 24;

      ResBmp.CanvasBGRA.CopyRect(Dst, Tileset, Src);
    end;

  begin
    Tileset := TilesetGraphics.Bitmaps[0];
    for I := Low(SubBlockIds) to High(SubBlockIds) do begin
      SB := SubBlockIds[I];
      X := (I mod 8) * 48;
      Y := (I div 8) * 48;

      if SB.IsWaterfall then
        DrawWaterfall
      else
        DrawNormalAT;

    end;
  end;

  procedure DrawTilesA2;
  var
    I, XTiles, YTiles: Integer;
    Src, Dst: TRect;
    Tileset: TBGRABitmap;
  begin
    Tileset := TilesetGraphics.Bitmaps[1];
    for I := 0 to 31 do begin
      XTiles := I mod 8;
      YTiles := I div 8;

      Dst.Left   := XTiles * 48; //TODO: adapt for different tile widths
      Dst.Top    := (YTiles + OffsetYInTiles) * 48; //TODO: adapt for different tile heights
      Dst.Right  := Dst.Left + 48;
      Dst.Bottom := Dst.Top + 48;

      if IncludeBackgrounds and ((XTiles = 1) or (XTiles = 3)) then
        DrawAutotileAsBackground(Tileset, Dst, XTiles - 1, YTiles);

      Src.Left   := XTiles * (48 * 2);
      Src.Top    := YTiles * (48 * 3);
      Src.Right  := Src.Left + 48;
      Src.Bottom := Src.Top + 48;

      ResBmp.CanvasBGRA.CopyRect(Dst, Tileset, Src);
    end;
  end;

  procedure DrawTilesA3;
  var
    I, XTiles, YTiles, DstLeft, DstTop, SrcLeft, SrcTop: Integer;
    Tileset: TBGRABitmap;
  begin
    Tileset := TilesetGraphics.Bitmaps[2];
    for I := 0 to 31 do begin
      XTiles := I mod 8;
      YTiles := I div 8;

      DstLeft   := XTiles * 48; //TODO: adapt for different tile widths
      DstTop    := (YTiles + OffsetYInTiles) * 48; //TODO: adapt for different tile heights

      SrcLeft   := XTiles * (48 * 2);
      SrcTop    := YTiles * (48 * 2);

      ResBmp.CanvasBGRA.CopyRect( //top left
        Rect(DstLeft, DstTop, DstLeft + 24, DstTop + 24),
        Tileset,
        Rect(SrcLeft, SrcTop, SrcLeft + 24, SrcTop + 24));
      ResBmp.CanvasBGRA.CopyRect( //top right
        Rect(DstLeft + 24, DstTop, DstLeft + 24 + 24, DstTop + 24),
        Tileset,
        Rect(SrcLeft + 72, SrcTop, SrcLeft + 72 + 24, SrcTop + 24));
      ResBmp.CanvasBGRA.CopyRect( //bottom left
        Rect(DstLeft, DstTop + 24, DstLeft + 24, DstTop + 24 + 24),
        Tileset,
        Rect(SrcLeft, SrcTop + 72, SrcLeft + 24, SrcTop + 72 + 24));
      ResBmp.CanvasBGRA.CopyRect( //bottom right
        Rect(DstLeft + 24, DstTop + 24, DstLeft + 24 + 24, DstTop + 24 + 24),
        Tileset,
        Rect(SrcLeft + 72, SrcTop + 72, SrcLeft + 72 + 24, SrcTop + 72 + 24));
    end;
  end;

  procedure DrawTilesA4;
  var
    I, XTiles, YTiles, DstLeft, DoubleYTiles, DstTop, SrcLeft, SrcTop: Integer;
    Tileset: TBGRABitmap;
  begin
    Tileset := TilesetGraphics.Bitmaps[3];
    for I := 0 to 48 do begin
      XTiles := I mod 8;
      YTiles := I div 8;

      DstLeft   := XTiles * 48; //TODO: adapt for different tile widths
      DstTop    := (YTiles + OffsetYInTiles) * 48; //TODO: adapt for different tile heights

      SrcLeft   := XTiles * (48 * 2);

      DoubleYTiles := (YTiles div 2);

      if YTiles mod 2 = 0 then begin
        SrcTop    := DoubleYTiles * (48 * 5);
        ResBmp.CanvasBGRA.CopyRect(
          Rect(DstLeft, DstTop, DstLeft + 48, DstTop + 48),
          Tileset,
          Rect(SrcLeft, SrcTop, SrcLeft + 48, SrcTop + 48));
      end
      else begin
        SrcTop    := DoubleYTiles * (48 * 5) + (3 * 48);

        ResBmp.CanvasBGRA.CopyRect( //top left
          Rect(DstLeft, DstTop, DstLeft + 24, DstTop + 24),
          Tileset,
          Rect(SrcLeft, SrcTop, SrcLeft + 24, SrcTop + 24));
        ResBmp.CanvasBGRA.CopyRect( //top right
          Rect(DstLeft + 24, DstTop, DstLeft + 24 + 24, DstTop + 24),
          Tileset,
          Rect(SrcLeft + 72, SrcTop, SrcLeft + 72 + 24, SrcTop + 24));
        ResBmp.CanvasBGRA.CopyRect( //bottom left
          Rect(DstLeft, DstTop + 24, DstLeft + 24, DstTop + 24 + 24),
          Tileset,
          Rect(SrcLeft, SrcTop + 72, SrcLeft + 24, SrcTop + 72 + 24));
        ResBmp.CanvasBGRA.CopyRect( //bottom right
          Rect(DstLeft + 24, DstTop + 24, DstLeft + 24 + 24, DstTop + 24 + 24),
          Tileset,
          Rect(SrcLeft + 72, SrcTop + 72, SrcLeft + 72 + 24, SrcTop + 72 + 24));
      end;
    end;
  end;

  procedure DrawTilesA5;
  var
    Tileset: TBGRABitmap;
  begin
    Tileset := TilesetGraphics.Bitmaps[4];
    ResBmp.CanvasBGRA.CopyRect(
      Rect(0, OffsetYInTiles * 48, Tileset.Width, OffsetYInTiles * 48 + Tileset.Height),
      Tileset,
      Rect(0, 0, Tileset.Width, Tileset.Height));
  end;

begin
  ResBmp := CreateBitmapForPalette(TILE_TYPE_A);
  ResBmp.CanvasBGRA.Brush.Color := clBtnFace;
  ResBmp.CanvasBGRA.FillRect(0, 0, ResBmp.Width, ResBmp.Height);

  OffsetYInTiles := 0;
  if TilesetGraphics.HasSet(tgstA1) then begin
    DrawTilesA1;
    Inc(OffsetYInTiles, 2);
  end;
  if TilesetGraphics.HasSet(tgstA2) then begin
    DrawTilesA2;
    Inc(OffsetYInTiles, 4);
  end;
  if TilesetGraphics.HasSet(tgstA3) then begin
    DrawTilesA3;
    Inc(OffsetYInTiles, 4);
  end;
  if TilesetGraphics.HasSet(tgstA4) then begin
    DrawTilesA4;
    Inc(OffsetYInTiles, 6);
  end;
  if TilesetGraphics.HasSet(tgstA5) then begin
    DrawTilesA5;
    Inc(OffsetYInTiles, 16);
  end;

  DrawTilesA := ResBmp;
end;

function TTilePaletteDrawer.DrawUpperTiles(TileType: Integer): TBGRABitmap;
var
  NameIndex: Integer;
  TilesetName: String;
  ResBmp: TBGRABitmap;
  Tileset: TBGRABitmap;
  Column: Integer;
begin
  NameIndex := TileType - TILE_TYPE_B + 5;
  ResBmp := CreateBitmapForPalette(TileType);
  ResBmp.Canvas.Brush.Color := clBtnFace;
  ResBmp.Canvas.FillRect(0, 0, ResBmp.Width, ResBmp.Height);
  Tileset := TilesetGraphics.Bitmaps[NameIndex];

  if Tileset <> nil then begin
    for Column := 0 to 1 do begin
      ResBmp.CanvasBGRA.CopyRect(
        Rect(0,  Column*16*48,  8*48,  Column*16*48 + Tileset.Height),
        Tileset,
        Rect(Column*8*48,  0,  (Column + 1)*8*48,  Tileset.Height)
        );
    end;
  end;

  DrawUpperTiles := ResBmp
end;


function TTilePaletteDrawer.GetPaletteHeight(TileType: Integer): Integer;
  function HeightForType(NameIndex: Integer; ResHeight: Integer): Integer;
  begin
    if TilesetGraphics.HasSet(NameIndex) then
      HeightForType := ResHeight
    else
      HeightForType := 0
  end;

  function CheckIfTileImageExists: Boolean;
  var
    NameIndex: Integer;
  begin
    NameIndex := TileType - TILE_TYPE_B + 5;
    CheckIfTileImageExists := TilesetGraphics.HasSet(NameIndex)
  end;

begin
  case TileType of
  TILE_TYPE_A:
    GetPaletteHeight := HeightForType(0, 2) + HeightForType(1, 4)
                        + HeightForType(2, 4)
                        + HeightForType(3, 6)
                        + HeightForType(4, 16);
  TILE_TYPE_B, TILE_TYPE_C, TILE_TYPE_D, TILE_TYPE_E: begin
      if CheckIfTileImageExists then
        GetPaletteHeight := 32
      else
        GetPaletteHeight := 0;
    end;
  TILE_TYPE_REGION:
    GetPaletteHeight := 32;
  else
    GetPaletteHeight := 0;
  end;
end;

end.

